import { ref, computed } from 'vue';
import { cloneDeep } from 'lodash-es';
import { isState, type State } from './constant';

// value.get, value.set
declare class Value<T = any> {
  get value(): WithTableSign<T>[];
  set value(value: T[]);
}

export type TableSign = {
  __id__: `${string}-${string}-${string}-${string}-${string}`;
  __state__: State;
};

export type WithTableSign<T> = T & TableSign;
export type DataSourceItem = WithTableSign<unknown>;

export type WithoutTableSign<WithTableSigna> = Omit<
  WithTableSigna,
  keyof TableSign
>;

export type DataSource<T> = Value<T> & {
  init(data: T[]): void;
  raw(): T[];
  copy(indexOrId: number | string): void;
  update(update: Partial<WithTableSign<T>>, index: number): void;
  remove(indexOrId: number | string): void;
  insert(raw: T, index?: number): void;
  indexOf: (idOrItem: string | DataSourceItem) => number;
};

// 格式化table-data
function toTableData<T>(data: T[]) {
  return data.map((item) => withTableSign(item));
}

// 给对象添加 __id__, __state__
function withTableSign<T>(
  raw: T
  // state: State = 'edit'
): WithTableSign<T> {
  const clone = cloneDeep(raw) as WithTableSign<T>;
  let state: State = 'edit';

  if (isState(clone.__state__)) {
    state = clone.__state__;
  }

  return Object.assign({}, clone, {
    __id__: crypto.randomUUID(),
    __state__: state,
  });
}

export function useDataSource<T = any>(
  initData?: T[]
): DataSource<T> {
  initData = initData ? toTableData(initData) : [];
  const dataSource = ref<WithTableSign<T>[]>(initData as any);

  const computedData = computed<any>({
    get() {
      return dataSource.value as any as WithTableSign<T>;
    },
    set(newValue) {
      const tableData = toTableData(newValue);
      dataSource.value = tableData as any;
    },
  });

  // 根据index插入数组，index没有则从尾部插入
  function insert(raw: T, index?: number) {
    const data = withTableSign(raw) as any;
    if (index) {
      dataSource.value.splice(index, 0, data);
    } else {
      dataSource.value.push(data);
    }
  }

  function copy(indexOrId: number | string) {
    if (typeof indexOrId === 'number') {
      copyByIndex(indexOrId);
    } else {
      copyById(indexOrId);
    }
  }

  // 根据index复制数组元素
  function copyByIndex(index: number) {
    const max = dataSource.value.length - 1;
    if (max < 0 || index > max || index < 0) return;

    const item = dataSource.value[index];
    if (!item) return;

    const copy = withTableSign(item);
    dataSource.value.splice(index + 1, 0, copy);
  }

  function copyById(id: string) {
    const index = dataSource.value.findIndex((e) => e.__id__ === id);
    if (index > -1) {
      copyByIndex(index);
    }
  }

  /**
   * 根据id或者数组索引删除一行
   */
  function remove(indexOrId: number | string) {
    if (typeof indexOrId === 'number') {
      removeByIndex(indexOrId);
    } else {
      removeById(indexOrId);
    }
  }

  function removeByIndex(index: number) {
    dataSource.value = dataSource.value.filter(
      (_, idx) => idx !== index
    );
  }

  function removeById(id: string) {
    dataSource.value = dataSource.value.filter(
      (item) => item.__id__ !== id
    );
  }

  // 设置值
  function indexOf(idOrItem: string | DataSourceItem) {
    if (typeof idOrItem === 'string') {
      return dataSource.value.findIndex((e) => e.__id__ === idOrItem);
    }
    return dataSource.value.indexOf(idOrItem as any);
  }

  // 原始值，不带__id__,__state__；
  function raw() {
    return dataSource.value.map((item) => {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { __id__, __state__, ...rawItem } = item;
      return rawItem;
    });
  }

  function update(update: Partial<WithTableSign<T>>, index: number) {
    const newItem = Object.assign(
      {},
      dataSource.value[index],
      update
    );
    dataSource.value[index] = newItem;
  }

  const methods: any = {
    insert,
    copy,
    update,
    remove,
    raw,
    indexOf,
  };

  return Object.assign(computedData, methods);
}

export function raw(data: any) {
  if (!data) return data;
  const isArray = Array.isArray(data);
  const pick = (obj: any) => {
    const { __id__, __state__, ...picked } = obj;
    return picked;
  };
  if (isArray) {
    return data.map((e) => pick(e));
  }
  return pick(data);
}
